package com.webshooter.map;

import com.webshooter.math.IntPair;
import com.webshooter.math.MathUtil;
import com.webshooter.math.Rectangle;
import com.webshooter.math.Vector;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import javax.ejb.Startup;
import javax.inject.Singleton;

@Singleton
@Startup
public class TileMap {

    public static final int tileWidth = 32;
    public static final int tileHeight = 32;
    public static final int horizontalNumTiles = 50;
    public static final int verticalNumTiles = 50;
    private Integer[][] tileMap = new Integer[horizontalNumTiles][verticalNumTiles];
    private List<Integer> tiles = Arrays.asList(27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 8, 8, 8, 8, 8, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 8, 27, 27, 27, 27, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 8, 27, 27, 27, 27, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 8, 27, 27, 27, 27, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 8, 8, 8, 8, 8, 8, 8, 27, 27, 27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27, 27, 8, 27, 27, 8, 27, 27, 27, 27, 8, 27, 27,
            27, 27, 27, 8, 27, 27, 8, 8, 8, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 8, 27, 27, 8, 27, 27, 27, 27, 8, 27, 27,
            27, 27, 27, 8, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 8, 27, 27, 8, 8, 8, 8, 8, 8, 27, 27,
            27, 27, 27, 8, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 8, 8, 27, 27, 27, 8, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
            27, 27, 27, 8, 27, 27, 8, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 8, 27, 27, 27, 8, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
            27, 27, 27, 8, 27, 27, 8, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 8, 27, 27, 27, 8, 27, 27, 8, 27, 27, 8, 8, 8, 8, 8, 8, 8, 8,
            8, 8, 8, 8, 27, 27, 8, 27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27, 27, 8, 27, 27, 27, 8, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
            27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 8, 8, 27, 27, 8, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
            27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 8, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 8, 138, 138, 8, 138, 138, 138, 138, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 8, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 8, 138, 138, 8, 138, 138, 138, 138, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 138, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 138, 138, 138, 8, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 8, 138, 138, 8, 138, 138, 138, 8, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 138, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 138, 138, 138, 8, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 8, 138, 138, 8, 138, 138, 138, 8, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 138, 138, 8, 138, 82, 82, 82, 82, 82, 82, 82, 82, 82, 8, 82, 82, 8, 82, 82, 138, 8, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 138, 138, 8, 138, 82, 82, 82, 82, 82, 82, 82, 82, 82, 8, 82, 82, 8, 82, 82, 138, 8, 138, 138, 27, 8, 27, 27,
            8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 82, 82, 82, 82, 82, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 138, 138, 82, 82, 82, 82, 82, 82, 82, 82, 82, 8, 82, 82, 8, 82, 82, 138, 8, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 82, 82, 82, 8, 138, 138, 138, 138, 82, 82, 82, 82, 82, 82, 82, 82, 82, 8, 82, 82, 8, 82, 82, 138, 8, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 82, 82, 82, 8, 138, 138, 8, 8, 8, 8, 8, 8, 8, 8, 82, 82, 82, 8, 82, 82, 8, 82, 82, 138, 8, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 82, 82, 8, 82, 82, 82, 82, 82, 82, 8, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 8, 82, 82, 8, 82, 82, 138, 8, 138, 138, 27, 8, 27, 27,
            8, 8, 27, 27, 8, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 82, 82, 82, 8, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 8, 82, 82, 8, 82, 82, 138, 8, 138, 138, 27, 8, 27, 27,
            8, 8, 27, 27, 8, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 82, 82, 82, 8, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 8, 82, 82, 8, 82, 82, 138, 8, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 8, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 82, 82, 82, 8, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 8, 82, 82, 8, 82, 82, 138, 8, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 8, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 82, 82, 82, 8, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 8, 82, 82, 8, 82, 82, 138, 8, 138, 138, 27, 27, 27, 27,
            27, 27, 27, 27, 8, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 8, 82, 82, 8, 8, 8, 8, 8, 138, 138, 27, 27, 27, 27,
            27, 27, 8, 8, 8, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 82, 82, 82, 82, 138, 138, 138, 138, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 138, 138, 138, 138, 27, 8, 27, 27,
            27, 27, 8, 8, 8, 27, 138, 8, 8, 8, 8, 8, 8, 82, 82, 8, 82, 82, 82, 82, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 138, 138, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 8, 138, 138, 8, 8, 8, 8, 8, 8, 82, 82, 8, 82, 82, 82, 82, 138, 138, 138, 138, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 8, 138, 138, 138, 138, 138, 138, 138, 82, 82, 82, 82, 82, 82, 82, 82, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 138, 138, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 8, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 27, 8, 27, 27,
            27, 27, 27, 27, 8, 138, 138, 138, 138, 138, 138, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 8, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 8, 138, 138, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27, 27,
            8, 8, 27, 27, 8, 138, 138, 138, 138, 138, 138, 8, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 8, 138, 138, 8, 138, 138, 138, 138, 138, 138, 138, 27, 27, 27, 27,
            8, 8, 27, 27, 8, 138, 138, 138, 138, 138, 138, 8, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 8, 138, 138, 8, 138, 138, 138, 138, 138, 138, 138, 27, 27, 27, 27,
            27, 27, 27, 27, 8, 138, 138, 138, 138, 138, 138, 8, 27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 138, 8, 8, 8, 8, 82, 82, 8, 82, 82, 8, 82, 82, 8, 8, 8, 8, 8, 8, 8, 27, 27,
            27, 27, 27, 27, 8, 138, 138, 138, 138, 138, 138, 8, 27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 138, 8, 8, 8, 8, 82, 82, 8, 82, 82, 8, 82, 82, 8, 8, 8, 8, 8, 8, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 82, 82, 82, 82, 82, 8, 82, 82, 82, 82, 82, 82, 138, 138, 138, 138, 27, 27, 27, 27,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 82, 82, 82, 8, 82, 82, 82, 82, 82, 82, 138, 138, 138, 138, 27, 27, 27, 27,
            27, 27, 27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 138, 8, 8, 8, 8, 8, 8, 8, 8, 8, 82, 82, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 82, 82, 82, 8, 82, 82, 82, 82, 82, 82, 138, 138, 138, 138, 27, 27, 27, 27,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 82, 82, 82, 82, 82, 82, 82, 82, 8, 82, 82, 82, 82, 82, 82, 138, 138, 138, 138, 27, 27, 27, 27,
            27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 8, 138, 138, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27, 27,
            27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 8, 27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 138, 138, 138, 138, 138, 8, 138, 138, 8, 138, 138, 8, 138, 138, 138, 138, 138, 138, 138, 27, 8, 27, 27,
            27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27, 27, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 138, 138, 8, 138, 138, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27, 27,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 8, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 27, 27, 27, 27,
            27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27);
    private List<Integer> solidTiles = Arrays.asList(8);
    private final int entityRadius = 20;
    private Random randomGenerator = new Random();
    private List<Vector> spawnPositions = Arrays.asList(new Vector(100, 100), new Vector(950, 100), new Vector(100, 950), new Vector(1000, 1000));

    public TileMap() {
        for (int i = 0; i < tiles.size(); i++) {
            int x = i % horizontalNumTiles;
            int y = i / verticalNumTiles;
            tileMap[x][y] = tiles.get(i);
        }
    }

    public Vector getRandomSpawnPosition() {
        return spawnPositions.get(randomGenerator.nextInt(spawnPositions.size()));
    }

    public boolean isPassable(IntPair indices) {
        return isInMap(indices.x, indices.y) && !solidTiles.contains(tileMap[indices.x][indices.y]);
    }

    public IntPair getTileCoords(Vector pos) {
        return new IntPair((int) Math.floor(pos.x / tileWidth), (int) Math.floor(pos.y / tileHeight));
    }

    public Vector calcCollisionCorrectedPosition(Vector entityPos, Vector movementVector) {
        Vector correctedPos = new Vector(entityPos);
        if (movingInX(correctedPos, movementVector.x)) {
            correctedPos.x += movementVector.x;
            if (movingInY(correctedPos, movementVector.y)) {
                correctedPos.y += movementVector.y;
            }
        } else if (movingInY(correctedPos, movementVector.y)) {
            correctedPos.y += movementVector.y;
            if (movingInX(correctedPos, movementVector.x)) {
                correctedPos.x += movementVector.x;
            }
        }
        return correctedPos;
    }

    private boolean movingInX(Vector entityPos, float dx) {
        if (dx == 0) {
            return false;
        } else {
            Vector targetPos = entityPos.add(new Vector(dx, 0));
            IntPair tileCoords = getTileCoords(targetPos);
            return !(isIntersectingAndBlocking(targetPos, entityRadius, new IntPair(tileCoords.x + (int) Math.signum(dx), tileCoords.y - 1))
                    || isIntersectingAndBlocking(targetPos, entityRadius, new IntPair(tileCoords.x + (int) Math.signum(dx), tileCoords.y))
                    || isIntersectingAndBlocking(targetPos, entityRadius, new IntPair(tileCoords.x + (int) Math.signum(dx), tileCoords.y + 1)));
        }
    }

    private boolean movingInY(Vector entityPos, float dy) {
        if (dy == 0) {
            return false;
        } else {
            Vector targetPos = entityPos.add(new Vector(0, dy));
            IntPair tileCoords = getTileCoords(targetPos);
            return !(isIntersectingAndBlocking(targetPos, entityRadius, new IntPair(tileCoords.x - 1, tileCoords.y + (int) Math.signum(dy)))
                    || isIntersectingAndBlocking(targetPos, entityRadius, new IntPair(tileCoords.x, tileCoords.y + (int) Math.signum(dy)))
                    || isIntersectingAndBlocking(targetPos, entityRadius, new IntPair(tileCoords.x + 1, tileCoords.y + (int) Math.signum(dy))));
        }
    }

    private boolean isIntersectingAndBlocking(Vector circle, int radius, IntPair tileIndices) {
        return intersect(circle, radius, getRectangleForTile(tileIndices)) && !isPassable(tileIndices);
    }

    private boolean intersect(Vector circle, int radius, Rectangle rectangle) {
        Vector closestEdge = new Vector(
                MathUtil.clamp(circle.x, rectangle.left, rectangle.right),
                MathUtil.clamp(circle.y, rectangle.top, rectangle.bottom));
        Vector distance = circle.subtract(closestEdge);
        return distance.normSquared() < (radius * radius);
    }

    private Rectangle getRectangleForTile(IntPair tileIndices) {
        int x = tileIndices.x;
        int y = tileIndices.y;
        return new Rectangle(x * tileWidth, (x + 1) * tileWidth, y * tileHeight, (y + 1) * tileHeight);
    }

    private boolean isInMap(int x, int y) {
        return 0 <= x && x < horizontalNumTiles
                && 0 <= y && y < verticalNumTiles;
    }
}
